package sdk3
{
	/**  ============== IMPORTS **/
	import flash.events.Event;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;
	
	import mx.rpc.xml.SimpleXMLDecoder;
	import mx.rpc.xml.SimpleXMLEncoder;
	import mx.utils.ObjectUtil;

	/*******************************************************************************
	 * Software: c_OBJECT
	 * Version:  1.0
	 * Date:     26.06.2009_TIME_15:12:49     
	 * WHO?
		 *  v1.1 11.01.2009_TIME_08:55:59
	 * WHAT?
		 * toolBox for object operations
	 * v1.1 02.01.2009_TIME_18:24:55
	  * LICENSE:  GNU General Public License (http://www.gnu.org/licenses/gpl.html) 	 *  	 * English: 		This program is free software; 		you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; 		either version 3 of the License, or (at your option) any later version.   		This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 		without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 		See the GNU General Public License for more details. 		You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.   		 * Excerpt English: 		 * Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), 		 * that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things   		Auf Deutsch: 		Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, 		weitergeben und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren Version. 		Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, 		sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. 		Details finden Sie in der GNU General Public License. 		Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm erhalten haben. 		Falls nicht, siehe <http://www.gnu.org/licenses/>.   		 * Auszug Deutsch: 		 * Wenn wir von freier Software sprechen, so beziehen wir uns auf Freiheit, nicht auf den Preis. 		 * Unsere Allgemeinen Öffentlichen Lizenzen sind darauf angelegt, sicherzustellen, daß Sie die Freiheit haben, 		 * Kopien freier Software zu verbreiten (und dafür etwas zu berechnen, wenn Sie möchten), die Möglichkeit, 		 * daß Sie die Software als Quelltext erhalten oder den Quelltext auf Wunsch bekommen, daß Sie die Software 		 * ändern oder Teile davon in neuen freien Programmen verwenden dürfen und daß Sie wissen, daß Sie dies alles tun dürfen. 
	 * Lizenz/AGBS: s.h. / see AGBS_LIZENZVERTRAG.txt
	 * 
	 * WHAT PURPOSE?
	 * 
	 * REQUIREMENTS TO RUN THIS:
	 *  
	 * USAGE EXAMPLE:
	 * 
	 * var c_OBJECT_inst:c_OBJECT = new c_OBJECT();
	 * c_OBJECT_inst.someMethod(OBJECT);
	 * 
	 * TODO:
	 * 
	 * COOL IDEAS:
	 * 
	 * KNOWN BUGS:
	 * 
	 * CHANGELOG:
	 * 
	 *******************************************************************************/

	public class c_OBJECT
	{
		public function c_OBJECT()
		{
		}

		private static var instance:c_OBJECT = null;
		
		public static function singleton():c_OBJECT
		{
			if (instance == null) 
			{
				instance = new c_OBJECT();
			}
			return instance;
		}
		
		/**  ============== FUNCTIONS **/
	
		/** converts an object to array 
		 * INPUT:
		 * STRING_Mode
		 * can be:
			 * key:value
			 *	 return both, key and value
			 * key
			 *	 return only keys of the object 
			 * value
			 *	 return only values of the object
		 * STRING_Delimiter (per default ':')
		 	* when doing key:value pairs how the key and values chould be concatenated, key-value, key,value or default key:value
		 * OUTPUT:
		 * array containing key, value or key:value of object.
		 * **/
		public function object_to_array(OBJECT:Object,STRING_Mode:String = 'key:value',STRING_Delimiter:String = ':'):Array
		{
			var ARRAY_Result:Array = new Array();
			
			// THIS WAY ONE ITERATES THROUGH THE OBJECT AND GET THE VALUES! (from key:value pairs)
			if(STRING_Mode == 'value')
			{
				for each (var STRING_Key:String in OBJECT)
				{
					// trace(STRING_Key);
				    ARRAY_Result.push(STRING_Key);
				}
			}
			
			// THIS WAY ONE ITERATES THROUGH THE OBJECT AND GET THE KEYS! (from key:value pairs)
			if(STRING_Mode == 'key')
			{
				for (var key:String in OBJECT)
				{
					// trace(key);
				    ARRAY_Result.push(key);
				}
			}
			
			if(STRING_Mode == 'key:value')
			{
				for (var key:String in OBJECT)
				{
				    // trace(key + STRING_Delimiter + OBJECT[key]);
				    ARRAY_Result.push(key + STRING_Delimiter + OBJECT[key]);
				}
			}
					
			return ARRAY_Result;
		}
		
		/** tests if the OBJECT given actually has properties.
		 * strings, numbers do not have properties **/
		public function hasProperties(OBJECT:Object):Boolean
		{
			return isObject(OBJECT);
		}

		/** tests if the OBJECT given actually has properties.
		 * strings, numbers do not have properties **/
		public function isObject(OBJECT:Object):Boolean
		{
			var RESULT:Boolean = false;
			try
			{
				for each (var STRING_Key:String in OBJECT)
				{
					// the object has properties
					RESULT = true;
					break;
				}
			}
			catch(e:Error)
			{
				RESULT = false;
			}
			return RESULT;
		}

		/** filters out all properties, but the given ones. 
		 * INPUT:
		 * OBJECT -> whoes properties needs filtering.
		 * PROPERTIES -> array with properties, these properties will remain, all other properties will be deleted.
		 * OUTPUT:
		 * OBJECT -> filtered object
		 * **/
		public function delete_properties_except(OBJECT:Object,PROPERTIES:Array):Object
		{
			var c_ARRAY_inst:c_ARRAY = new c_ARRAY();
			var OBJECT_RESULT:Object = new Object();
			
			var ARRAY_OBJECTA_VALUES:Array = new Array();
			var ARRAY_OBJECTA_PROPERTIES:Array = new Array();			
			for (var key:String in OBJECT)
			{
				if(c_ARRAY_inst.IN_ARRAY(PROPERTIES,key))
				{
					OBJECT_RESULT[key] = OBJECT[key];
				}
			}

			return OBJECT_RESULT;
		}
		/** copies/adds/overwrites all properties from OBJECTA -to-> OBJECTB
		 * INPUT:
		 * OBJECTA, OBJECTB
		 * OUTPUT:
		 * OBJECTB, an OBJECT, that contains the properties of A and the properties of B.
		 * UNTESTED! v1.0
		 * **/
		public function copy_properties(OBJECTA:Object,OBJECTB:Object,overwrite:Boolean = true):Object
		{
			/** key, values von OBJECTA auslesen **/
			for (var KEY:String in OBJECTA)
			{
				if(overwrite)
				{
					try
					{
						OBJECTB[KEY] = OBJECTA[KEY];
					}
					catch(e:Error)
					{
					}
				}
				else
				{
					if(!OBJECTB.hasOwnProperty(KEY))
					{
						try
						{
							OBJECTB[KEY] = OBJECTA[KEY];
						}
						catch(e:Error)
						{
						}
						
					}
				}
			}
					
			return OBJECTB;
		}
		
		/** same function , different name, merges all properties from OBJECTA into OBJECTB **/
		public function merge(OBJECTA:Object,OBJECTB:Object,overwrite:Boolean = true):Object
		{
			return copy_properties(OBJECTA,OBJECTB,overwrite);
		}
		
		/** 
		 * UNTESTED! 
		 * converts an object to a string with custom delimiter 
		 * INPUT:
		 * STRING_Mode
		 * can be:
		 * key:value (default)
		 *	 return both, key and value
		 * key
		 *	 return only keys of the object 
		 * value
		 *	 return only values of the object
		 * 
		 * PROPERTY_DELIM (per default ',')
		 * when doing key:value pairs how the pairs should be concatenated, key:value,key:value,key:value is default.
		 * 
		 * KEY_VALUE_DELIM (per default ':')
		 * when doing key:value pairs how the key and values chould be concatenated, key-value, key,value or default key:value
		 * 
		 * RECURSIVENESS (default "none" possible values: 1, 2, 3.... unlimited)
		 * defines if sub-objects and sub-arrays with possible objects should also be parsed and serialized to a string.
		 * 
		 * OUTPUT:
		 * array containing key, value or key:value of object.
		 * 
		 * EXAMPLE:
		 * chatUserGroups = c_OBJECT_inst.toString2(chatUserGroups_OBJECT,"key:value",",",":","unlimited"); 
		 * **/
		public function toString2(OBJECT:Object,STRING_Mode:String = 'key:value',PROPERTY_DELIM:String = ',',KEY_VALUE_DELIM:String = ':',RECURSIVENESS:String = "none"):String
		{
			return object_to_string(OBJECT,STRING_Mode,PROPERTY_DELIM,KEY_VALUE_DELIM,RECURSIVENESS);
		}

		/** 
		 * UNTESTED! 
		 * converts an object to a string with custom delimiter 
		 * INPUT:
		 * STRING_Mode
		 * can be:
			 * key:value (default)
			 *	 return both, key and value
			 * key
			 *	 return only keys of the object 
			 * value
			 *	 return only values of the object
			 * 
		 * PROPERTY_DELIM (per default ',')
		 	* when doing key:value pairs how the pairs should be concatenated, key:value,key:value,key:value is default.
			 * 
		 * KEY_VALUE_DELIM (per default ':')
		 	* when doing key:value pairs how the key and values chould be concatenated, key-value, key,value or default key:value
			 * 
		 * RECURSIVENESS (default "none" possible values: 1, 2, 3.... unlimited)
		 	* defines if sub-objects and sub-arrays with possible objects should also be parsed and serialized to a string.
			 * 
		 * OUTPUT:
		 * array containing key, value or key:value of object.
		 * 
		 * EXAMPLE:
		 	* chatUserGroups = c_OBJECT_inst.toString2(chatUserGroups_OBJECT,"key:value",",",":","unlimited"); 
		 * **/
		public function serialize(OBJECT:Object,STRING_Mode:String = 'key:value',PROPERTY_DELIM:String = ',',KEY_VALUE_DELIM:String = ':',RECURSIVENESS:String = "none"):String
		{
			 return object_to_string(OBJECT,STRING_Mode = 'key:value',PROPERTY_DELIM = ',',KEY_VALUE_DELIM = ':',RECURSIVENESS = "none");
		}
		
		public var RECURSIVENESS:Number = 0;
		private var CURRENT_RECURSION:Number = 0;
		/** 
		 * UNTESTED! 
		 * converts an object to a string with custom delimiter 
		 * INPUT:
		 * STRING_Mode
		 * can be:
			 * key:value (default)
			 *	 return both, key and value
			 * key
			 *	 return only keys of the object 
			 * value
			 *	 return only values of the object
			 * 
		 * PROPERTY_DELIM (per default ',')
		 	* when doing key:value pairs how the pairs should be concatenated, key:value,key:value,key:value is default.
			 * 
		 * KEY_VALUE_DELIM (per default ':')
		 	* when doing key:value pairs how the key and values chould be concatenated, key-value, key,value or default key:value
			 * 
		 * RECURSIVENESS (default "none" possible values: 1, 2, 3.... unlimited)
		 	* defines if sub-objects and sub-arrays with possible objects should also be parsed and serialized to a string.
			 * 
		 * OUTPUT:
		 * array containing key, value or key:value of object.
		 * 
		 * EXAMPLE:
		 	* chatUserGroups = c_OBJECT_inst.toString2(chatUserGroups_OBJECT,"key:value",",",":","unlimited"); 
		 * **/
		public function object_to_string(OBJECT:Object,STRING_Mode:String = 'key:value',PROPERTY_DELIM:String = ',',KEY_VALUE_DELIM:String = ':',RECURSIVENESS:String = "none"):String
		{
			var c_STRING_inst:c_STRING = new c_STRING();
			if(RECURSIVENESS == "unlimited")
			{
				this.RECURSIVENESS = Infinity;
			}
			else if(isNaN(parseFloat(RECURSIVENESS)))
			{
				this.RECURSIVENESS = 0;
			}
			else
			{
				this.RECURSIVENESS = parseFloat(RECURSIVENESS);
			}

			if(OBJECT != null)
			{
				var ARRAY_Result:Array = new Array();
				var STRING_Result:String = '';
				
				// THIS WAY ONE ITERATES THROUGH THE OBJECT AND GET THE VALUES! (from key:value pairs)
				if(STRING_Mode == 'value')
				{
					for each (var STRING_Key:String in OBJECT)
					{
						// trace(STRING_Key);
					    ARRAY_Result.push(STRING_Key);
					}
				}
				
				// THIS WAY ONE ITERATES THROUGH THE OBJECT AND GET THE KEYS! (from key:value pairs)
				if(STRING_Mode == 'key')
				{
					for (var key:String in OBJECT)
					{
						// trace(key);
					    ARRAY_Result.push(key);
					}
				}
				
				if(STRING_Mode == 'key:value')
				{
					for (var key:String in OBJECT)
					{
						if(RECURSIVENESS != "none")
						{
							if(hasProperties(OBJECT[key]))
							{
								// recurse into object's properties if there are any
								if((this.CURRENT_RECURSION < this.RECURSIVENESS) || (RECURSIVENESS == "unlimited"))
								{
									var intermediateRESULT:String = object_to_string(OBJECT[key],STRING_Mode,PROPERTY_DELIM,KEY_VALUE_DELIM+KEY_VALUE_DELIM,RECURSIVENESS);
									ARRAY_Result.push(key + KEY_VALUE_DELIM + intermediateRESULT);
								}
							}
							else if(OBJECT[key] is Array)
							{
								// recurse into ARRAY's elements if there are any
								for each(var ELEMENT:Object in OBJECT[key] as Array)
								{
									if((this.CURRENT_RECURSION < this.RECURSIVENESS) || (RECURSIVENESS == "unlimited"))
									{
										var intermediateRESULT:String = object_to_string(OBJECT[key],STRING_Mode,PROPERTY_DELIM,KEY_VALUE_DELIM+KEY_VALUE_DELIM,RECURSIVENESS);
										ARRAY_Result.push(key + KEY_VALUE_DELIM + intermediateRESULT);
									}
								}
							}
							else
							{
							    ARRAY_Result.push(key + KEY_VALUE_DELIM + OBJECT[key]);
							}
						}
						else
						{
						    ARRAY_Result.push(key + KEY_VALUE_DELIM + OBJECT[key]);
						}
					}
				}

				STRING_Result = ARRAY_Result[0];
				for(var counter:int = 0;!(counter == ARRAY_Result.length);counter++)
				{
					STRING_Result = STRING_Result + PROPERTY_DELIM + ARRAY_Result[counter];
				}
			}

			CURRENT_RECURSION++;

			return STRING_Result;
		}
		
		/** detects if an Object is empty
		 * if object is a string
		 * it is considered 'empty' when it is '' or null or undefined
		 * if object is a object
		 * it is considered 'empty' when it is null or undefined
		 * if object is a number
		 * it is considered 'empty' when it undefined, if it is NaN it is not empty.
		 * 
		 * INPUT:
		 * OBJECT -> the object that is examined.
		 * EMPTY_VALUES -> an array containing values that also mean 'empty' == true -> result in true.
		 * **/
		 public function empty(OBJECT:Object,EMPTY_VALUES:Array = null):Boolean
		 {
		 	var c_ARRAY_inst:c_ARRAY = new c_ARRAY();
		 	var RESULT:Boolean = false;
		 	var FOUND:Boolean = false;
		 	
		 	/** check if OBJECT complies with any of the values given in EMPTY_VALUES **/
		 	if(EMPTY_VALUES != null)
		 	{
				if(((OBJECT is String) == false) && (OBJECT != null) && ((OBJECT is Number) == false) && ((OBJECT is int) == false))
				{
					trace('stop');
				}
				RESULT = FOUND = c_ARRAY_inst.IN_ARRAY(EMPTY_VALUES,OBJECT,'boolean');
		 	}
	 		
		 	/** check if OBJECT complies with any of the values given in EMPTY_VALUES **/
	 		if(FOUND == false)
	 		{
			 	/** string **/
			 	if(OBJECT == null || OBJECT == undefined)
			 	{
			 		RESULT = true;
			 	}
			 	else if(OBJECT is String)
			 	{
			 		
			 		if(OBJECT == '' || OBJECT == null || OBJECT == undefined)
			 		{
			 			RESULT = true;
			 		}
			 		else
			 		{
			 			RESULT = false;
			 		}
			 	}
			 	else if(OBJECT is Number)
			 	{
				 	/** number **/
			 		if(OBJECT == null || OBJECT == undefined)
			 		{
			 			RESULT = true;
			 		}
			 		else
			 		{
			 			RESULT = false;
			 		}
			 	}
			 	else if(OBJECT is Array)
			 	{
		 		 	/** Array **/
			 		if(OBJECT == null || OBJECT['length'] == 0)
			 		{
			 			RESULT = true;
			 		}
			 		else
			 		{
			 			RESULT = false;
			 		}
			 	}
			 	else if(OBJECT is Boolean)
			 	{
		 		 	/** Boolean **/
			 		if(OBJECT == true || OBJECT == false)
			 		{
			 			RESULT = false;
			 		}
			 		else
			 		{
			 			/** everything else except true/false is considered empty **/
			 			RESULT = true;
			 		}
			 	}
			 	else if(OBJECT is Object)
			 	{
	 	 		 	/** der erkennt alles als Object an, deswegen object ganz zu letzt. **/
			 		if(OBJECT == null || OBJECT == undefined || OBJECT == '')
			 		{
			 			RESULT = true;
			 		}
			 		else
			 		{
			 			RESULT = false;
			 		}
			 	}
			 	else if(OBJECT == null || OBJECT == undefined)
			 	{
			 		RESULT = true;
			 	}
	 		}
		 	
		 	return RESULT;
		 }

		 /** just an alias for function empty **/
		 public function is_empty(OBJECT:Object,EMPTY_VALUES:Array = null):Boolean
		 {
		 	return empty(OBJECT,EMPTY_VALUES);
		 }
		 
		 /** just an alias for function is_empty **/
		 public function is_not_empty(OBJECT:Object,EMPTY_VALUES:Array = null):Boolean
		 {
		 	return not_empty(OBJECT,EMPTY_VALUES);
		 }

		/** detects if an Object is NOT empty (inverse of empty) 
		 * INPUT:
		 * OBJECT -> the object that is examined.
		 * EMPTY_VALUES -> an array containing values that also mean 'empty' == true -> result in true.
		 * **/
		 public function not_empty(OBJECT:Object,EMPTY_VALUES:Array = null):Boolean
		 {
		 	var BOOLEAN_Result:Boolean = empty(OBJECT,EMPTY_VALUES);
		 	if(BOOLEAN_Result)
		 	{
		 		BOOLEAN_Result = false;
		 	}
		 	else
		 	{
		 		BOOLEAN_Result = true;
		 	}
		 	
		 	return BOOLEAN_Result;
		 }

		/** event enthält ein element welches ein selected property hat (zB. checkbox)
		 * ist dieses element selected == true
		 * werden alle anderen elemente im ARRAY selected = false gesetzt. **/ 
		public function EXCLUSIVE_CHECKBOX(event:Event,ARRAY:Array,MODE:String = 'SWITCH'):void
		{
			var CLICKED:Object = event.currentTarget;
			
			if(CLICKED != null)
			{
				if(CLICKED['selected'] == true)
				{
					
				}
				
				for(var counter:int = 0;!(counter == ARRAY.length);counter++)
				{
					var OBJECT:Object = ARRAY[counter];
					OBJECT['selected'] = false;
				}
			}
		}
		
		/** read style properties **/
		public function READ_STYLE_PROPERTIES(DISPLAY_OBJECT:Object):Object
		{
			var RESULT:Object = new Object(); 
			if(DISPLAY_OBJECT.hasOwnProperty('text'))
			{
				RESULT['COLOR'] = DISPLAY_OBJECT.getStyle('color');
				RESULT['fontFamily'] = DISPLAY_OBJECT.getStyle('fontFamily');
				RESULT['fontSize'] = DISPLAY_OBJECT.getStyle('fontSize');
				RESULT['textAlign'] = DISPLAY_OBJECT.getStyle('textAlign');
				if(empty(RESULT['fontFamily']))
				{
					RESULT['fontFamily'] = 'Arial';
				}

				if(empty(RESULT['fontSize']))
				{
					RESULT['fontSize'] = 10;
				}
			}
			
			return RESULT;
		}
		
		/** v1.7
		 * yes we can create an independent instances/copy of primitives or arrays or objects,
		 * even objects that have null or undefined as key or value. ObjectUtil.copy currently (FlexSDK 3.4) fails to copy. (it copies only half of the original object if it contains keys == null)
		 * 
		 * erstellt eine unabhängige instanz eines beliebigen primitiven typs, array's, object's
		 * sogar von objects die null or undefined als key oder value haben. ObjectUtil.copy kann solche objects aktuell (FlexSDK 3.4) nicht kopieren. (es kopiert nur die hälfte des original objects)
		 * 
		 * features added:
		 * also respects input null and undefined.
		 * 
		 * limitation:
		 * it fails on copying displayobjects. anyone knows some workaround?
		 *  
		  * LICENSE:  GNU General Public License (http://www.gnu.org/licenses/gpl.html) 	 *  	 * English: 		This program is free software; 		you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; 		either version 3 of the License, or (at your option) any later version.   		This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 		without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 		See the GNU General Public License for more details. 		You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.   		 * Excerpt English: 		 * Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), 		 * that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things   		Auf Deutsch: 		Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, 		weitergeben und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren Version. 		Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, 		sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. 		Details finden Sie in der GNU General Public License. 		Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm erhalten haben. 		Falls nicht, siehe <http://www.gnu.org/licenses/>.   		 * Auszug Deutsch: 		 * Wenn wir von freier Software sprechen, so beziehen wir uns auf Freiheit, nicht auf den Preis. 		 * Unsere Allgemeinen Öffentlichen Lizenzen sind darauf angelegt, sicherzustellen, daß Sie die Freiheit haben, 		 * Kopien freier Software zu verbreiten (und dafür etwas zu berechnen, wenn Sie möchten), die Möglichkeit, 		 * daß Sie die Software als Quelltext erhalten oder den Quelltext auf Wunsch bekommen, daß Sie die Software 		 * ändern oder Teile davon in neuen freien Programmen verwenden dürfen und daß Sie wissen, daß Sie dies alles tun dürfen.  u may use this source and do with it whatever you want ,but do not remove the credits and do not be evil! may the source be with you.
		 * 
		 *  @param value Object that should be copied.
		 *  @param value String MODE can be 'do not copy keys that are null' (default, filter those keys out because other functions may fail on such objects as well) or 'copy null'.
		 *  @return Copy of the specified Object.
		 * 
		 * CHANGES:
		 	* o now also copies DATE-Objects successfully. 
		 * **/
		public function copy(OBJECT:*,MODE:String = 'do not copy keys that are null'):*
		{
			if(OBJECT === undefined)
			{
				return undefined;
			}
			else if(OBJECT === null)
			{
				return null;
			}
			else if(OBJECT is String)
			{
				var INDEPENDENT_STRING:String = new String();
				INDEPENDENT_STRING = OBJECT;
				return INDEPENDENT_STRING;
			}
			else if(OBJECT is Number)
			{
				var INDEPENDENT_NUMBER:Number = new Number();
				INDEPENDENT_NUMBER = OBJECT;
				return INDEPENDENT_NUMBER;
			}
			else if(OBJECT is int)
			{
				var INDEPENDENT_INT:int = new int();
				INDEPENDENT_INT = OBJECT;
				return INDEPENDENT_INT;
			}
			else if(OBJECT is Boolean)
			{
				var INDEPENDENT_BOOLEAN:Boolean = new Boolean();
				INDEPENDENT_BOOLEAN = OBJECT;
				return INDEPENDENT_BOOLEAN;
			}
			else if(OBJECT is Array)
			{
				var INDEPENDENT_ARRAY:Array = new Array();
				var STRING_Key:String = '';
				for(STRING_Key in OBJECT)
				{
				    /** just in case ARRAY is consisting of objects, also make independent copies of those objects **/
				    /** filters out null-keys because many other functions fail on processing objects with null as an key **/
				    /** just in case ARRAY is consisting of objects, also make independent copies of those objects **/
				    if((STRING_Key === null) || (STRING_Key === undefined) || (STRING_Key === ''))
				    {
				    	if(MODE == 'copy null')
				    	{
						    INDEPENDENT_ARRAY[STRING_Key] = copy(OBJECT[STRING_Key],MODE);
				    	}
				    }
				    else
				    {
					    INDEPENDENT_ARRAY[STRING_Key] = copy(OBJECT[STRING_Key],MODE);
				    }
				}
	
				if(OBJECT === undefined)
				{
					return undefined;
				}
				else if(OBJECT === null)
				{
					return null;
				}
				else
				{
					return INDEPENDENT_ARRAY;
				}
			}
			else
			{
				var STRING_Key:String = '';
				var INDEPENDENT_OBJECT:Object = null;
				if(OBJECT is Date)
				{
					INDEPENDENT_OBJECT = new Date();
				}
				else
				{
					INDEPENDENT_OBJECT = new Object();
				}
					
				/** alternativel: also is able to copy null == key objects that ObjectUtil.copy( fails to copy (yet) **/
				for(STRING_Key in OBJECT)
				{
				    /** just in case ARRAY is consisting of objects, also make independent copies of those objects **/
				    /** filters out null-keys because many other functions fail on processing objects with null as an key **/
				    if((STRING_Key === null) || (STRING_Key === undefined) || (STRING_Key === ''))
				    {
				    	if(MODE == 'copy null')
				    	{
						    INDEPENDENT_OBJECT[STRING_Key] = copy(OBJECT[STRING_Key],MODE);
				    	}
				    }
				    else
				    {
					    INDEPENDENT_OBJECT[STRING_Key] = copy(OBJECT[STRING_Key],MODE);
				    }
				}

				if(OBJECT === undefined)
				{
					return undefined;
				}
				else if(OBJECT === null)
				{
					return null;
				}
				else
				{
					return INDEPENDENT_OBJECT;
				}
			}
		}

		public function DETECT_NULL_PROPERTY_IN_OBJECT(OBJECT:Object):Boolean
		{
			var RESULT:Boolean = false;
			for(var property:Object in OBJECT)
			{
				if(property == null)
				{
					RESULT = true;
					break;
				}
			}
			return RESULT;
		}

		public function DETECT_NULL_PROPERTY_IN_ARRAY(ARRAY:Array):Object
		{
			var RESULT:Object = {detected:false,at:undefined};
			var OBJECT:Object = new Object();
			for(var counter:int = 0;!(counter == ARRAY.length);counter++)
			{
				OBJECT = ARRAY[counter];	
				if(DETECT_NULL_PROPERTY_IN_OBJECT(OBJECT))
				{
					RESULT['at'] = counter;
					RESULT['detected'] = true;
					break;
				}
			}
			
			return RESULT;
		}

		/** REMOVE_PROPERTY PROPERTY:String from OBJECT:Object **/
	public function REMOVE_PROPERTY(PROPERTY:String,OBJECT:Object):Object
		{
			var RESULT:Object = copy(OBJECT);
			delete RESULT[PROPERTY];
/*			var RESULT:Object = new Object();
			for(var KEY:String in OBJECT)
			{
				if(KEY != PROPERTY)
				{
					RESULT[KEY] = OBJECT[KEY];
				}
			}
*/			
			return RESULT;
		}
		
		/** remove multiple properties from an object PROPERTY:String from OBJECT:Object **/
		public function REMOVE_PROPERTIES(PROPERTIES:Array,OBJECT:Object):Object
		{
			var RESULT:Object = copy(OBJECT);
			
			for each(var PROPERTY:String in PROPERTIES)
			{
				delete RESULT[PROPERTY];
			}
/*			var RESULT:Object = new Object();
			for(var KEY:String in OBJECT)
			{
				if(KEY != PROPERTY)
				{
					RESULT[KEY] = OBJECT[KEY];
				}
			}
*/			
			return RESULT;
		}
		
		/** copies properties without overwriting the original properties,
		 * meaning it copies only properties and values from A to B if they don't exist in B
		 * WARNING! UNTESTED!
		 * **/
		public function copy_from_A_to_B_without_overwriting(ObjectA:Object,ObjectB:Object):Object
		{
			for(var KEY:String in ObjectA)
			{
				if(ObjectB.hasOwnProperty(KEY) == false)
				{
					ObjectB[KEY] = ObjectA[KEY];
				}
			}
			
			return ObjectB;
		}
		
		/** convert object to xml
		 * 
		 * 
		 * example:
			chatUserGroups_OBJECT = Object (@1a9708a9)	
				ARRAY = Array (@1ad6f0b1)	
					[0] = 1	
					[1] = 2	
					[2] = 3	
					[3] = 4	
					length = 4	
				blockedUsers = Array (@1ab92971)	
					[0] = "user1"	
					[1] = "user2"	
					[2] = "user3"	
					length = 3	
				favouriteUsers = Array (@1ab92e09)	
					[0] = "user4"	
					[1] = "user5"	
					[2] = "user6"	
					length = 3	
				PROPERTY = Object (@1ab528d1)	
					SUBOBJECT = "SUBPROB"	
					SUBSUBOBJECT = Object (@1ab52999)	
						KEY = "VALUE"	

 			<root>
			  <ARRAY>
			    <item>1</item>
			    <item>2</item>
			    <item>3</item>
			    <item>4</item>
			  </ARRAY>
			  <blockedUsers>
			    <item>user1</item>
			    <item>user2</item>
			    <item>user3</item>
			  </blockedUsers>
			  <favouriteUsers>
			    <item>user4</item>
			    <item>user5</item>
			    <item>user6</item>
			  </favouriteUsers>
			  <PROPERTY>
			    <SUBOBJECT>SUBPROB</SUBOBJECT>
			    <SUBSUBOBJECT>
			      <KEY>VALUE</KEY>
			    </SUBSUBOBJECT>
			  </PROPERTY>
			</root>
		 * **/
		public function toXML(OBJECT:Object):XML
		{
			var qName:QName = new QName("root");
			var xmlDocument:XMLDocument = new XMLDocument();
			var simpleXMLEncoder:SimpleXMLEncoder = new SimpleXMLEncoder(xmlDocument);
			var xmlNode:XMLNode = simpleXMLEncoder.encodeValue(OBJECT, qName, xmlDocument);
			var xml:XML = new XML(xmlDocument.toString());
			// trace(xml.toXMLString());
			return xml;
		}

		/** tries to convert XML to Flex-Object **/
		public function fromXML(xml:XML):Object
		{
			var c_XML_inst:c_XML = new c_XML();
			var RESULT:Object = c_XML_inst.XMLDocument_TO_OBJECT(xml);
			return RESULT;
		}
	}
}